#include <thread>
#include <iostream>
#include "Notifier.h"

using namespace SystemTool;

// Construct the obj
Notifier::Notifier()
{
    m_pLock = new std::mutex;
    m_pHandlerContainer = new std::vector<IHandler*>;
    m_pTaskLock = new std::mutex;
    m_pThread = new std::thread;
    m_pbIsExit = new std::atomic<bool>;
    m_pbIsExit->store(false);
    m_pvTaskContainer = new std::queue<NArgs>;
    StartThread();
}

// Destruct the obj
Notifier::~Notifier()
{
    // Stop the thread
    StopThread();
    if(m_pThread)
    {
        delete m_pThread;
        m_pThread = nullptr;
    }
    
    // Clear the queue
    ClearTask();
    if (m_pvTaskContainer)
    {
        delete m_pvTaskContainer;
        m_pvTaskContainer = nullptr;
    }
    if(m_pTaskLock)
    {
        delete m_pTaskLock;
        m_pTaskLock = nullptr;
    }

    // Clear the table
    Clear();
    if (m_pHandlerContainer)
    {
        delete m_pHandlerContainer;
        m_pHandlerContainer = nullptr;
    }
    if (m_pLock)
    {
        delete m_pLock;
        m_pLock = nullptr;
    }
}

// Add to table
void Notifier::Add(IHandler *pHandler)
{
    std::lock_guard<std::mutex> Locker(*m_pLock);
    if(m_pHandlerContainer == nullptr)
    {
        return;
    }

    if(m_pHandlerContainer->empty())
    {
        m_pHandlerContainer->push_back(pHandler);
        return;
    }

    auto Iter = std::find_if(m_pHandlerContainer->begin(), m_pHandlerContainer->end(), [&pHandler](IHandler* pItem) {
        if(pHandler == pItem)
        {
            return true;
        }
        return false;
    });

    if(Iter != m_pHandlerContainer->end())
    {
        m_pHandlerContainer->erase(Iter);
    }
    m_pHandlerContainer->push_back(pHandler);
}

// Remove from the table
void Notifier::Remove(IHandler *pHandler)
{
    std::lock_guard<std::mutex> Locker(*m_pLock);
    if(m_pHandlerContainer == nullptr)
    {
        return;
    }

    if(m_pHandlerContainer->empty())
    {
        return;
    }

    auto Iter = std::find_if(m_pHandlerContainer->begin(), m_pHandlerContainer->end(), [&pHandler](IHandler* pItem) {
        if(pHandler == pItem)
        {
            return true;
        }
        return false;
    });

    if(Iter != m_pHandlerContainer->end())
    {
        m_pHandlerContainer->erase(Iter);
    }
}

// Clear the table
void Notifier::Clear()
{
    std::lock_guard<std::mutex> Locker(*m_pLock);
    m_pHandlerContainer->clear();
}

// Send the notification
void Notifier::Send(NArgs &Args)
{
    PushTask(Args);
}

// Add the task
void Notifier::PushTask(NArgs &arg)
{
    std::lock_guard<std::mutex> Locker(*m_pTaskLock);

    if(m_pvTaskContainer == nullptr)
    {
        return;
    }
    m_pvTaskContainer->push(arg);
}

// Get the task
NArgs Notifier::PopTask()
{
    std::lock_guard<std::mutex> Locker(*m_pTaskLock);

    if(m_pvTaskContainer == nullptr)
    {
        return NArgs();
    }
    NArgs arg = m_pvTaskContainer->front();
    m_pvTaskContainer->pop();
    return arg;
}

// Clear the task
void Notifier::ClearTask()
{
    std::lock_guard<std::mutex> Locker(*m_pTaskLock);

    if (m_pvTaskContainer == nullptr)
    {
        return;
    }

    while (!m_pvTaskContainer->empty())
    {
        m_pvTaskContainer->pop();
    }
}

// Handle task
void Notifier::HandleTask(NArgs& Args)
{
    std::vector<IHandler *> vTempContainer;
    {
        std::lock_guard<std::mutex> Locker(*m_pLock);
        if (m_pHandlerContainer == nullptr)
        {
            return;
        }
        if (m_pHandlerContainer->empty())
        {
            return;
        }
        vTempContainer = *m_pHandlerContainer;
    }

    for (size_t index = 0; index < vTempContainer.size(); index++)
    {
        IHandler *pHandler = vTempContainer.at(index);
        if(pHandler == nullptr)
        {
            continue;
        }

        // Maybe it is a empty task
        if (Args.pArgs == nullptr || Args.iArgSize <= 0)
        {
            continue;
        }

        // Broacast to every one if do not identify receiver
        IHandler *pReceiver = (IHandler *)Args.pReceiver;
        if (pReceiver == nullptr)
        {
            pHandler->Process(Args);
            continue;
        }

        // Notify some one
        if(pHandler == pReceiver)
        {
           pHandler->Process(Args);
           return;
        }
    }
}

// Run task
void Notifier::Run()
{
    if(m_pbIsExit == nullptr)
    {
        return;
    }

    while (1)
    {
        if(m_pbIsExit->load() == true)
        {
            std::cout << "Break the thread!\n";
            break;
        }

        // Get the task
        NArgs Args = PopTask();

        // Handle the task
        HandleTask(Args);

        // Release the task memory
        Args.DestroyMemory();

        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    std::cout << "Quit the notifier thread!\n";
}

// Start the thread
void Notifier::StartThread()
{
    if(m_pThread == nullptr)
    {
        return;
    }
    *m_pThread = std::thread(&Notifier::Run, this);
}

// Stop the thread
void Notifier::StopThread()
{
    if (m_pbIsExit)
    {
        m_pbIsExit->store(true);
    }

    if(m_pThread)
    {
        if(m_pThread->joinable())
        {
            m_pThread->join();
        }
    }
}
